package chemical_formula.interpreter;

import chemical_formula.val.*;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class Judgment {
    Map<String, Integer> visit(AST node) throws Exception {
        String[] strings = node.getClass().getName().split("\\.");
        String className = strings[strings.length-1];

        Method visitMethod = this.getClass().getDeclaredMethod("visit"+className, AST.class);
        return (Map<String, Integer>) visitMethod.invoke(this, node);
    }

    Map<String, Integer> visitElement(AST node) {
        Element element = (Element)node;
        Map<String, Integer> map = new HashMap<>();
        map.put(element.elementStr, 1);
        return map;
    }

    Map<String, Integer> visitTermNode(AST node) throws Exception {
        TermNode termNode = (TermNode)node;
        int count = termNode.count;
        Map<String, Integer> map = visit(termNode.term);
        map.replaceAll((k, v) -> map.get(k) * count);
        return map;
    }

    Map<String, Integer> visitFormulaNode(AST node) throws Exception {
        FormulaNode formulaNode = (FormulaNode)node;
        int count = formulaNode.count;
        Map<String, Integer> map = new HashMap<>();
        for (TermNode termNode : formulaNode.termList) {
            Map<String, Integer> termMap = visit(termNode);
            for (String key : termMap.keySet()) {
                if (map.containsKey(key)){
                    map.put(key, map.get(key)+termMap.get(key));
                }
                else {
                    map.put(key, termMap.get(key));
                }
            }
        }
        map.replaceAll((k, v) -> map.get(k) * count);
        return map;
    }

    Map<String, Integer> visitExprNode(AST node) throws Exception {
        ExprNode exprNode = (ExprNode)node;
        Map<String, Integer> map = new HashMap<>();
        for (FormulaNode formulaNode : exprNode.formulaList) {
            Map<String, Integer> formulaMap = visit(formulaNode);
            for (String key : formulaMap.keySet()) {
                if (map.containsKey(key)){
                    map.put(key, map.get(key)+formulaMap.get(key));
                }
                else {
                    map.put(key, formulaMap.get(key));
                }
            }
        }
        return map;
    }

    public boolean judge(EqualOp equalOp) throws Exception {
        Map<String, Integer> leftMap = visit(equalOp.left);
        Map<String, Integer> rightMap = visit(equalOp.right);
        return leftMap.equals(rightMap);
    }
}
